Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metal Rendering on macOS #1326

Merged
merged 165 commits into from
Nov 29, 2023
Merged

Metal Rendering on macOS #1326

merged 165 commits into from
Nov 29, 2023

Conversation

colincornaby
Copy link
Contributor

@colincornaby colincornaby commented Feb 17, 2023

Update:

Welcome back! Now that the Mac client is merged, we're just down to the Metal bits.

There are still some major things missing from this PR that people should be aware of. This will be a milestone - but not quite the endpoint yet for work.

  • Resolution switching does not work in this PR. There is an attempt to implement this in Virtual Display Mode Support #1244. Some things are going to be wonky because the client doesn't know the current display mode. The Mysterium demo included Virtual Display Mode Support #1244.
  • The Mac client cannot patch itself and uses the Windows patch data. Being dealt with elsewhere.
  • There are some build issues that can be worked around on Apple Silicon.
  • Cross compiling between architectures still does not work.
  • The shaders are direct ports of the HLSL 1.1 assembly. They're not written for readability - they're meant to give the exact same results as DX. I have a branch going with ongoing work to sanitize this code. The code also backs off of exact numerical matching and implements things like fast sin instead of Cyan's sin approximation code. That may not end up as part of this PR.

This PR is very much a draft. Getting this out there so people can look and give early feedback. A few known things:

  • Formatting is a bit of a mess. Still looking at Clang format to clean things up. Some of this was written before I understood the projects conventions.
  • The Mac client code itself is a bit of a mess. It still needs more cleanup.
  • There is a shader cache subsystem that I'm not completely happy with. I haven't quite figured out how to clean it up. It's a giant hash table that takes shader attributes and retrieves/caches the right shader program based on that. I think maybe it could be done smarter. And possibly also have a shared implementation with GL.
  • The Mac client can't do executable patching yet.
  • There's no launcher with the Mac client. It's all one app. So far - this is intended.
  • The Mac client can't patch unless the account has admin privs. There is no privilege escalation built in.
  • GPU based character skinning is not yet in this branch (looking more likely I'll pull it in.)
  • Resolution controls and frame buffer scaling is not yet in this branch, so Retina support is still shaky. (Parts of this are being considered on the backing-scale-changes branch.

There are probably more things I've forgotten as well. Again - early draft PR.

@colincornaby
Copy link
Contributor Author

I'm also fine with breaking this PR down into smaller chunks where possible.


#include "plMetalDevice.h"
#include "plSurface/plShaderTable.h"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire thing is the shader program cache that I am very much not in love with. These structures are hashable representations of different pipeline states. This is to prevent lurching as new materials are loaded. If a program suitable for rendering a material was already created, it's just reused.

This also could mean that an initial set of common programs could be precompiled before the age even loads. In practice, there aren't very many.

Comment on lines 66 to 75
@autoreleasepool {
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
NSOperatingSystemVersion version = processInfo.operatingSystemVersion;
NSString *versionString = [NSString stringWithFormat:@"%li.%li.%li", (long)version.majorVersion, (long)version.minorVersion, version.patchVersion];
devRec.SetDriverVersion([versionString cStringUsingEncoding:NSUTF8StringEncoding]);
}
devRec.SetDriverDesc(device->name()->utf8String());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance we could use hsSystemInfo::GetOperatingSystem() here (and avoid contaminating our C++ code with Objective C)?

It would give slightly different output ("macOS 11.0" vs "11.0.0") but should still be reasonable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't think that would be an issue. I don't think this string is used for anything critical?

I'm not sure Obj-C++ is the worst thing, but I understand the hesitation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooooof. hsSystemInfo::GetOperatingSystem() actually uses private API. I'd discourage its use until it can be fixed. That would be a blocker if the iOS App Store was ever a thing (which I know is a longer conversation.) In since there is a working implementation it might be better to stick with that for now. I can also look at rolling GetOperatingSystem() over to public API. That might require some Obj-C++ though.

Copy link
Contributor

@dgelessus dgelessus Feb 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the private APIs, we discussed this a bit when hsSystemInfo was originally added: #1014 (comment)

/System/Library/CoreServices/SystemVersion.plist exists on both macOS and iOS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NSProcessInfo is the definitive way to do this under "modern" Cocoa. However, I'd still want to do some amount of digging before making a decision. IGetAppleVersion could be moved to Obj-C++.
https://developer.apple.com/documentation/foundation/nsprocessinfo/1410906-operatingsystemversion

Stuff like _kCFSystemVersionProductNameKey is actively being rejected by Apple. Not necessarily the highest priority in since this isn't going through the App Store (right now.) But I also wouldn't want to leave a risk like that lurking in the code for someone else to stumble upon.

fCurrentRenderTargetCommandEncoder = nil;

//look up the shader by sigma value
MPSImageGaussianBlur *blur = (MPSImageGaussianBlur *)fBlurShaders[sigma];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shame this stuff isn't part of metal-cpp (or even the metal-cpp extensions for Apple's demo projects) 😞

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a good reminder for me that these shader programs aren't properly lifetime managed with the device.

Copy link
Member

@dpogue dpogue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We maybe want to rename the plClient subfolder from OSX to macOS since it's not 2014 anymore and they're changed their branding slightly.

There are a few parts of this that might conflict with #1325 or #1201 where the exact same changes have happened in the same files, but those should be easy enough to resolve.

I think we still want to have a discussion around how metal-cpp gets included. I don't think we love the idea of bundling it, so an automatic CMake fetch from a known URL might be the least bad option.

Sources/Plasma/PubUtilLib/plGImage/plMipmap.cpp Outdated Show resolved Hide resolved
Sources/Plasma/PubUtilLib/plAudio/plAudioSystem_Private.h Outdated Show resolved Hide resolved
Copy link
Contributor

@dgelessus dgelessus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some things (mostly minor) that I noticed while looking over the non-graphics bits...

CMakeLists.txt Outdated Show resolved Hide resolved
CMakeLists.txt Show resolved Hide resolved
Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Comment on lines 194 to 195
plIMouseXEventMsg* pXMsg = new plIMouseXEventMsg;
plIMouseYEventMsg* pYMsg = new plIMouseYEventMsg;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could be local variables instead of allocated via new, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did that in the Windows client, but there might be some threading going on here for macOS

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This did used to be threaded on macOS - but I reverted it back to a main thread only model. There were some weird side effects going on. It could move back to a render thread model in the future though. I'll take a look at this chunk though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this. In plMouseDevice these are also being created as pointers, and the function for sending the mouse message expects a pointer.

Sources/Plasma/FeatureLib/inc/pfAllCreatables.h Outdated Show resolved Hide resolved
Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Comment on lines 159 to 146
MACOSX_BUNDLE_BUNDLE_VERSION "0.1"
MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These (in theory) should match the values we use for the plProduct stuff:

Plasma/CMakeLists.txt

Lines 25 to 32 in 2457af4

set(PRODUCT_BRANCH_ID "1" CACHE STRING "Branch ID")
set(PRODUCT_BUILD_ID "918" CACHE STRING "Build ID")
set(PRODUCT_BUILD_TYPE "50" CACHE STRING "Build Type")
set(PRODUCT_CORE_NAME "UruLive" CACHE STRING "Product Core Name")
set(PRODUCT_SHORT_NAME "UruLive" CACHE STRING "Product Short Name")
set(PRODUCT_LONG_NAME "Uru Live" CACHE STRING "Product Long Name")
set(PRODUCT_UUID "ea489821-6c35-4bd0-9dae-bb17c585e680"
CACHE STRING "Product UUID")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MACOSX_BUNDLE_BUNDLE_VERSION should likely be some sort of build version. It's supposed to always increment so I'd have to figure out how to combine the data into a number that always goes up.

For MACOSX_BUNDLE_SHORT_VERSION_STRING - do we have an actual version number thats derived anywhere?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/H-uru/Plasma/blob/master/cmake/BuildInfo.cmake is used to embed git information into the executable, but that's not currently available in project mode.

Comment on lines 173 to 184
target_link_libraries(plClient PRIVATE "-framework Cocoa")
target_link_libraries(plClient PRIVATE "-framework QuartzCore")
target_link_libraries(plClient PRIVATE "-framework MetalPerformanceShaders")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can probably be specified below with the rest of the target_link_libraries using syntax like $<$<PLATFORM_ID:Darwin>:"-framework Cocoa">

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking into this. I'd like to figure out if there is a Mac specific platform ID so I can make sure Cocoa is only imported on macOS. (Still thinking about a future iOS build.) There probably is, I just need to look it up.

Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Comment on lines 194 to 195
plIMouseXEventMsg* pXMsg = new plIMouseXEventMsg;
plIMouseYEventMsg* pYMsg = new plIMouseYEventMsg;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did that in the Windows client, but there might be some threading going on here for macOS

Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp Outdated Show resolved Hide resolved
Sources/Plasma/NucleusLib/pnInputCore/plInputMap.cpp Outdated Show resolved Hide resolved
@colincornaby colincornaby force-pushed the apple-metal branch 2 times, most recently from 698daf0 to ca7b78b Compare February 19, 2023 07:27
@colincornaby
Copy link
Contributor Author

Main.mm isn't usually supposed to be this loaded in a Cocoa app. It would probably be good to start factoring things out into Cocoa classes.

@colincornaby
Copy link
Contributor Author

I think we still want to have a discussion around how metal-cpp gets included. I don't think we love the idea of bundling it, so an automatic CMake fetch from a known URL might be the least bad option.

I had the same thought. I'll think through if there are any better options. I'm not entirely sure if Apple's URLs are guaranteed to be stable.

@colincornaby
Copy link
Contributor Author

Renamed the "OSX" directory to "Mac-Cocoa."

Very likely if this ever makes it to iPad, I'll end up writing a new client in SwiftUI. "Mac-Cocoa" signifies a Mac Client written in Cocoa/AppKit. This leaves space for an "Apple-SwiftUI" client or something later.

Copy link
Member

@Hoikas Hoikas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

16/172 files viewed

Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Comment on lines 159 to 146
MACOSX_BUNDLE_BUNDLE_VERSION "0.1"
MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/H-uru/Plasma/blob/master/cmake/BuildInfo.cmake is used to embed git information into the executable, but that's not currently available in project mode.

Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
Sources/Plasma/Apps/plClient/CMakeLists.txt Outdated Show resolved Hide resolved
@colincornaby
Copy link
Contributor Author

Also a reminder that I'm not sure that the Mac keyboard event code works properly with internationalized keyboards. There's only a keymap for English. I remember a while ago there was discussion about USB HID keyboard ids. Mac is currently using the Mac key codes with its own lookup tables between the Plasma and Mac codes.

Apple's Game Controller library also incudes keyboard support meant specifically for games - and it's possible that it would have a cleaner integration path. But that API is only available from macOS 11 onwards. The existing Mac key codes system should probably be sufficient for now. An iPad port would likely implement Game Controller keyboard integration - and might share that implementation with newer versions of macOS.

@dgelessus
Copy link
Contributor

Also a reminder that I'm not sure that the Mac keyboard event code works properly with internationalized keyboards. There's only a keymap for English.

I haven't tested the Mac version recently, but the code looks to me like it should work properly regardless of what keyboard layout you're using (as long as it has Latin letters on it). PLSKeyboardEventMonitor passes both the key code (layout-neutral) and the character (layout-dependent) on to plInputManager::HandleKeyEvent, and from there on everything should behave the same as on Windows.

@colincornaby
Copy link
Contributor Author

macOS builds coming out of CI aren't viable right now. Not sure why. They're missing all the embedded libraries.

@colincornaby
Copy link
Contributor Author

Actually - I might be wrong. Does the Github Actions/vcpkg workflow link everything statically? I'm used to seeing dynamic libraries but it might just be that static linking is occurring.

@dpogue
Copy link
Member

dpogue commented Mar 5, 2023

We try to do static linking with vcpkg to cut down on the number of DLLs that need to be shipped for Windows clients.

@colincornaby
Copy link
Contributor Author

Python might be a bit of a problem. I think we've avoided dealing with it so far. Dynamically linked Python included all of the Python .so files in the app bundle, so they were all signed and trusted.

Statically linked Python puts them all in the Python/system folder. This means they're not signed, so the app won't link to them. It's also kind of discouraged to link outside your app bundle, but not impossible.

Would it be possible to have the Mac version build against and embed Python.framework? I'm also not sure what the role of Python.pak is and if any executables live in there.

@colincornaby
Copy link
Contributor Author

This also might be of interest, but is outside the vcpkg ecosystem:

https://github.com/beeware/Python-Apple-support

@dgelessus dgelessus mentioned this pull request Jul 13, 2023
@colincornaby colincornaby force-pushed the apple-metal branch 2 times, most recently from f3e4d1e to 4c91c8a Compare August 15, 2023 02:09
@colincornaby colincornaby changed the title macOS Client + Metal Rendering Metal Rendering on macOS Aug 15, 2023
@colincornaby colincornaby marked this pull request as ready for review August 15, 2023 02:11
@colincornaby
Copy link
Contributor Author

@dpogue - Guessing that you'd like to see the Metal-cpp headers moved out of tree. I'll start thinking about what to do. In since it's a header library I don't know that it's critical that it should be managed elsewhere. But as third party code it does make reviews messy.

The render command encoder is create lazily - and clear is not a draw call in Metal. A clear only pass meant no render was being encoded for the command buffer.

The render command encoder management could use a bit of cleaning - but for now manually forcing the lazy creation of a command encoder. This could be probably moved to a non-lazy model - but it’s lazy right now because we’re holding for a clear command.
Copy link
Member

@Hoikas Hoikas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Grass.metal is missing the CWE license block
  • Metal files still have code style issues, mostly around stray whitespace and brackets needing to be on a new line.
  • CMakeLists.txt line 62 spurious whitespace around parens
  • CMakeLists.txt FetchContent call - indents are 4 spaces
  • plMetalDeviceRef.h 292 zero initialization doesn't need to be explicit
  • plMetalMaterialShaderRef::EncodeArguments std::function parameters should probably be const references.
  • plMetalPipeline only base class destructors should be marked virtual.
  • Spurious whitespace in plMetalPipeline.h
  • plMetalPlateManager.cpp 52 zero initialization doesn't need to be explicit

Copy link
Member

@Hoikas Hoikas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like 7848944 messed up the whitespace in the license block of Avatar.metal.

A bit of strategy - do we want to merge #1284 first, remove the .clang-format files from this PR, then merge this one?

@colincornaby
Copy link
Contributor Author

I updated the clang-format file in #1284 so it could be merged first.

@Hoikas
Copy link
Member

Hoikas commented Nov 28, 2023

Ok, the .clang-format file is in, now that #1284 is merged. I think that, once the .clang-format files are culled from this PR, I will be 👍 on this. Then, we can start refining stuff in future PRs.

Copy link
Member

@Hoikas Hoikas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If anyone has any concerns about merging this as-is, please say so ASAP. The mac and metal work is not complete, but this, IMO, will serve as a good baseline for continuing work.

Copy link
Member

@dpogue dpogue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm 👍 on merging this, and sorting further things out in followup PRs.

I'll also rebase the OpenGL work after this is merged and see if I can consolidate anything.

@Hoikas Hoikas merged commit 6491a18 into H-uru:master Nov 29, 2023
17 checks passed
@Hoikas
Copy link
Member

Hoikas commented Nov 29, 2023

💥 💥 💥

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants